//
//  Five100px.swift
//  Photomania
//
//  Created by Essan Parto on 2014-09-25.
//  Copyright (c) 2014 Essan Parto. All rights reserved.
//

import UIKit
import Alamofire

@objc public protocol ResponseCollectionSerializable {
    static func collection(#response: NSHTTPURLResponse, representation: AnyObject) -> [Self]
}

/*
这个协议定义了返回集合的一个类方法（而不是单个元素），在本例中，返回的是[self]。完成处理方法将集合作为其第三个参数，即[T]，接着调用类型上的collection而不是调用初始化方法
*/
extension Alamofire.Request {
  public func responseCollection<T: ResponseCollectionSerializable>(completionHandler: (NSURLRequest, NSHTTPURLResponse?, [T]?, NSError?) -> Void) -> Self {
    let serializer: Serializer = { (request, response, data) in
      let JSONSerializer = Request.JSONResponseSerializer(options: .AllowFragments)
      let (JSON: AnyObject?, serializationError) = JSONSerializer(request, response, data)
      if response != nil && JSON != nil {
        return (T.collection(response: response!, representation: JSON!), nil)
      } else {
        return (nil, serializationError)
      }
    }
    
    return response(serializer: serializer, completionHandler: { (request, response, object, error) in
      completionHandler(request, response, object as? [T], error)
    })
  }
}

@objc public protocol ResponseObjectSerializable {
  init(response: NSHTTPURLResponse, representation: AnyObject)
}

/*
我们添加了.responseObject()函数。作为一个通用函数，它能够序列化所有符合ResponseObjectSerializable协议的数据对象。

这意味着，如果我们定义一个含有init(response:representation:)初始化方法的新类，那么 Alamofire 就能够自行从服务器返回该类型的对象。这时候，我们已经将序列化逻辑封装进了自定义类的内部。
*/
extension Alamofire.Request {
  public func responseObject<T: ResponseObjectSerializable>(completionHandler: (NSURLRequest, NSHTTPURLResponse?, T?, NSError?) -> Void) -> Self {
    let serializer: Serializer = { (request, response, data) in
      let JSONSerializer = Request.JSONResponseSerializer(options: .AllowFragments)
      let (JSON: AnyObject?, serializationError) = JSONSerializer(request, response, data)
      if response != nil && JSON != nil {
        return (T(response: response!, representation: JSON!), nil)
      } else {
        return (nil, serializationError)
      }
    }
    
    return response(serializer: serializer, completionHandler: { (request, response, object, error) in
      completionHandler(request, response, object as? T, error)
    })
  }
}

//创建自定义响应序列化方法(Serializer)
extension Alamofire.Request {
  class func imageResponseSerializer() -> Serializer {
    return { request, response, data in
      if data == nil {
        return (nil, nil)
      }
      
      let image = UIImage(data: data!, scale: UIScreen.mainScreen().scale)
      
      return (image, nil)
    }
  }
  
  func responseImage(completionHandler: (NSURLRequest, NSHTTPURLResponse?, UIImage?, NSError?) -> Void) -> Self {
    return response(serializer: Request.imageResponseSerializer(), completionHandler: { (request, response, image, error) in
      completionHandler(request, response, image as? UIImage, error)
    })
  }
}

struct Five100px {
  /*
  就是我们所创建的路由，它为我们的 API 调用方法创建合适的URLString实例。它是一个简单的遵守URLRequestConertible协议的enum类型，这个协议是在 Alamofire 当中定义的。当有枚举类型采用该协议的时候，该类型就必须含有一个名为URLRequest的NSURLRequest类型变量。
  
  这个路由含有两个静态常量：API 的baseURLString以及consumerKey。（最后一次声明，请PASTE_YOUR_CONSUMER_KEY_HERE替换为您自己的消费者密钥）现在，这个路由可以在必要的时候向最终的URLString中添加消费者密钥。
  
  您的应用拥有三个 API 终点(endpoint)：一个用来取出热门照片列表，一个用来取出某个特定照片的具体信息，一个用来取出某个照片的评论。路由将会借助三个相应的case声明来处理这三个终结点，每个终结点都会接收一到两个参数。
  
  我们已经定义了var URLRequest: NSURLRequest作为计算(computed)属性。这意味着每次我们使用enum的时候，它都会构造出基于特定case和其参数的最终 URL。
  */
  enum Router: URLRequestConvertible {
    static let baseURLString = "https://api.500px.com/v1"
    static let consumerKey = "0iSnepfo21yi6A3Gb6YDNDE6xWa6DVmxJqCT9pto"
    
    case PopularPhotos(Int)
    case PhotoInfo(Int, ImageSize)
    case Comments(Int, Int)
    
    var URLRequest: NSURLRequest {
      let (path: String, parameters: [String: AnyObject]) = {
        switch self {
        case .PopularPhotos (let page):
          let params = ["consumer_key": Router.consumerKey, "page": "\(page)", "feature": "popular", "rpp": "50",  "include_store": "store_download", "include_states": "votes"]
          return ("/photos", params)
        case .PhotoInfo(let photoID, let imageSize):
          var params = ["consumer_key": Router.consumerKey, "image_size": "\(imageSize.rawValue)"]
          return ("/photos/\(photoID)", params)
        case .Comments(let photoID, let commentsPage):
          var params = ["consumer_key": Router.consumerKey, "comments": "1", "comments_page": "\(commentsPage)"]
          return ("/photos/\(photoID)/comments", params)
        }
        }()
      
      let URL = NSURL(string: Router.baseURLString)
      let URLRequest = NSURLRequest(URL: URL!.URLByAppendingPathComponent(path))
      let encoding = Alamofire.ParameterEncoding.URL
      
      return encoding.encode(URLRequest, parameters: parameters).0
    }
  }
  
  enum ImageSize: Int {
    case Tiny = 1
    case Small = 2
    case Medium = 3
    case Large = 4
    case XLarge = 5
  }
}

class PhotoInfo: NSObject, ResponseObjectSerializable {
  let id: Int
  let url: String
  
  var name: String?
  
  var favoritesCount: Int?
  var votesCount: Int?
  var commentsCount: Int?
  
  var highest: Float?
  var pulse: Float?
  var views: Int?
  var camera: String?
  var desc: String?
  
  init(id: Int, url: String) {
    self.id = id
    self.url = url
  }
  
  required init(response: NSHTTPURLResponse, representation: AnyObject) {
    self.id = representation.valueForKeyPath("photo.id") as! Int
    self.url = representation.valueForKeyPath("photo.image_url") as! String
    
    self.favoritesCount = representation.valueForKeyPath("photo.favorites_count") as? Int
    self.votesCount = representation.valueForKeyPath("photo.votes_count") as? Int
    self.commentsCount = representation.valueForKeyPath("photo.comments_count") as? Int
    self.highest = representation.valueForKeyPath("photo.highest_rating") as? Float
    self.pulse = representation.valueForKeyPath("photo.rating") as? Float
    self.views = representation.valueForKeyPath("photo.times_viewed") as? Int
    self.camera = representation.valueForKeyPath("photo.camera") as? String
    self.desc = representation.valueForKeyPath("photo.description") as? String
    self.name = representation.valueForKeyPath("photo.name") as? String
  }
  
  override func isEqual(object: AnyObject!) -> Bool {
    return (object as! PhotoInfo).id == self.id
  }
  
  override var hash: Int {
    return (self as PhotoInfo).id
  }
}

/*
这段代码让Comment遵守ResponseCollectionSerializable协议，因此它将和上面的响应序列化方法协同工作。
*/
final class Comment: ResponseCollectionSerializable {
  @objc static func collection(#response: NSHTTPURLResponse, representation: AnyObject) -> [Comment] {
    var comments = [Comment]()
    
    for comment in representation.valueForKeyPath("comments") as! [NSDictionary] {
      comments.append(Comment(JSON: comment))
    }
    
    return comments
  }
  
  let userFullname: String
  let userPictureURL: String
  let commentBody: String
  
  init(JSON: AnyObject) {
    userFullname = JSON.valueForKeyPath("user.fullname") as! String
    userPictureURL = JSON.valueForKeyPath("user.userpic_url") as! String
    commentBody = JSON.valueForKeyPath("body") as! String
  }
}